package com.block.source.service.impl;

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.block.common.annotate.RedisLock;
import com.block.common.constant.RedisKeyConstant;
import com.block.common.enums.InfoTypeEnum;
import com.block.common.exception.NotifyExceptionService;
import com.block.common.model.vo.ResultInfoVO;
import com.block.common.oss.config.OssAliConfig;
import com.block.common.oss.model.OssAuthInfoParam;
import com.block.common.oss.service.IOssService;
import com.block.common.oss.service.impl.OssAliService;
import com.block.common.service.IApplicationInfoService;
import com.block.common.service.IChannelCommonService;
import com.block.common.service.LogFactory;
import com.block.common.service.merchant.IMerchantThirdBaseInfoService;
import com.block.datapush.service.IMemberPushOrderInfoService;
import com.block.datapush.special.enums.OrderStatusEnum;
import com.block.db.entity.*;
import com.block.db.mapper.MerchantThirdProtocolInfoMapper;
import com.block.source.model.dto.SourceMatchResultDTO;
import com.block.source.model.dto.SourceRegisterResultDTO;
import com.block.source.service.*;
import com.block.test.reqbean.source.CheckUserInfo;
import com.block.test.reqbean.source.ProtocolInfo;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.InputStream;
import java.net.URL;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

@Service
public class SourceRegisterUserService implements ISourceRegisterUserService {

    private static final Logger log = LogFactory.getSourceLog();
    private static final Logger unionLog = LogFactory.getUnionLog();

    @Autowired
    ISourcePushResultInfoService sourcePushResultInfoService;
    @Autowired
    ISourceMemberDataInfoTempService sourceMemberDataInfoTempService;
    @Autowired
    ISourceMemberDataInfoService sourceMemberDataInfoService;
    @Autowired
    IApplicationInfoService applicationInfoService;
    @Autowired
    ISourceMatchHandleService sourceMatchHandleService;
    @Autowired
    NotifyExceptionService notifyExceptionService;
    @Autowired
    IMerchantThirdBaseInfoService merchantThirdBaseInfoService;
    @Resource
    MerchantThirdProtocolInfoMapper merchantThirdProtocolInfoMapper;
    @Autowired
    IMemberPushOrderInfoService memberPushOrderInfoService;
    @Autowired
    IChannelCommonService channelCommonService;
    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Override
    @RedisLock(lockName = "SOURCE:REGISTER:ORDER:", key = "#orderNo")
    public ResultInfoVO sourceRegisterUser1(String orderNo, SourceBaseInfo sourceBaseInfo, CheckUserInfo userDataParam) {

        try {
            CountDownLatch countDownLatch = new CountDownLatch(1);
            Future<ResultInfoVO> merchantInfoFuture = ThreadUtil.execAsync(new Callable<ResultInfoVO>() {
                @Override
                public ResultInfoVO call() throws Exception {
                    ResultInfoVO resultInfoVO = sourceRegisterUser(orderNo, sourceBaseInfo, userDataParam);
                    countDownLatch.countDown();
                    return resultInfoVO;
                }
            });
            // 撞库时间
            long registerTimeout = sourceBaseInfo.getRegisterTimeout() - 100;
            countDownLatch.await(registerTimeout, TimeUnit.MILLISECONDS);

            // 进行执行完成，删除mobileMd5 撞库锁
            // 添加撞库锁
            if (StrUtil.isEmpty(userDataParam.getMobileMd5())){
                userDataParam.setMobileMd5(SecureUtil.md5(userDataParam.getMobile()).toLowerCase());
            }
            String mobileLockKey = StrUtil.format(RedisKeyConstant.SOURCE_CHECK_MOBILEMD5_LOCK_KEY, userDataParam.getMobileMd5());
            stringRedisTemplate.delete(mobileLockKey);


            if (merchantInfoFuture.isDone()) {
                ResultInfoVO resultInfoVO = merchantInfoFuture.get();
                return resultInfoVO;
            }
            // 异步执行，更新结果
            unionLog.info("【进件超时】【channelCode】{}【channelName】{}【orderNo】{}", sourceBaseInfo.getChannelCode(), sourceBaseInfo.getChannelName(), orderNo);

        } catch (Exception exception) {
            log.error("注册操作异常,", exception);
        }
        return ResultInfoVO.error("进件失败");
    }

    @Override
    public ResultInfoVO sourceRegisterUser(String orderNo, SourceBaseInfo sourceBaseInfo, CheckUserInfo checkUserInfo) {

        log.info("【线索注册】【channelCode】{}【channelName】{}【checkUserInfo】{}",
                sourceBaseInfo.getChannelCode(), sourceBaseInfo.getChannelName(), JSON.toJSONString(checkUserInfo));

        SourcePushResultInfo pushResultInfo = sourcePushResultInfoService.getById(checkUserInfo.getOrderNo());
        if (ObjectUtil.isEmpty(pushResultInfo)) {
            return ResultInfoVO.error("订单号错误");
        }
        if (!StrUtil.equals(OrderStatusEnum.INIT.getValue(), pushResultInfo.getStatus())) {
            return ResultInfoVO.error("订单状态错误");
        }
        // 进件有效期30分钟
        boolean timeoutFlag = LocalDateTime.now().isAfter(pushResultInfo.getCreateTime().plusMinutes(30L));
        if (timeoutFlag) {
            return ResultInfoVO.error("订单超时");
        }
        // 不进行手机号的查重，只要能撞库成功的数据，都进行接收; 进件的时候，删除用户资质信息，存储最新的资质信息，用于传输给下游
        String mobileMd5 = SecureUtil.md5(checkUserInfo.getMobile()).toLowerCase();
        SourceMemberDataInfoTemp memberDataInfoTemp = sourceMemberDataInfoTempService.getById(pushResultInfo.getDataId());
        if (!StrUtil.equals(mobileMd5, memberDataInfoTemp.getMobileMd5())) {
            return ResultInfoVO.error("与撞库手机号不同");
        }

        /// 保存用户资质信息
        ResultInfoVO resultInfoVO = sourceMemberDataInfoTempService.updateDataInfo(memberDataInfoTemp, checkUserInfo, pushResultInfo);
        log.info("【线索注册】保存用户资质信息【resultInfoVO】{}", JSON.toJSONString(resultInfoVO));
        if (!resultInfoVO.isSuccess()) {
            return resultInfoVO;
        }

        JSONObject data = resultInfoVO.getData();
        MemberExtInfo memberExtInfo = data.getObject("memberExtInfo", MemberExtInfo.class);

        // 继续操作订单信息
        SourceMatchResultDTO resultDTO = new SourceMatchResultDTO();
        resultDTO.setOrderNo(pushResultInfo.getOrderNo());
        resultDTO.setProtocolInfoList(checkUserInfo.getProtocolInfoList());

        //处理注册信息
        SourceRegisterResultDTO registerResultDTO = sourceMatchHandleService.handleRegisterUser(sourceBaseInfo, resultDTO, memberExtInfo);
        log.info("【线索注册】执行注册【registerResultDTO】{}", JSON.toJSONString(registerResultDTO));
        if (!registerResultDTO.isSuccess()) {
            return ResultInfoVO.error("信息注册失败");
        }

        // 保存用户协议
        if (ObjectUtil.isNotEmpty(checkUserInfo.getProtocolInfoList())){
            this.handleProtocolStorge(pushResultInfo, checkUserInfo.getProtocolInfoList(), memberExtInfo);
        }

        // 请求成功，根据线索类型，判断是否需要返回
        JSONObject returnObject = new JSONObject();

        if (StrUtil.equals(sourceBaseInfo.getType(), InfoTypeEnum.APIB.getValue())) {
            // url 授权回调
            ChannelInfo channelInfo = channelCommonService.getChannelInfoCache(memberDataInfoTemp.getChannelCode());
            ApplicationInfo appInfoByAppCode = applicationInfoService.getAppInfoByType(channelInfo.getAppType());
            String approveUrl = StrUtil.format(appInfoByAppCode.getApproveUrl(), pushResultInfo.getId());

            SourcePushResultInfo updateResult = new SourcePushResultInfo();
            updateResult.setId(pushResultInfo.getId());
            updateResult.setStatus(OrderStatusEnum.WAIT_APPROVE.getValue());
            updateResult.setApproveUrl(approveUrl);
            sourcePushResultInfoService.updateById(updateResult);

            returnObject.put("status", "000");
            returnObject.put("remark", "进件成功");
            returnObject.put("approveUrl", approveUrl);
        } else {
            // api直接推送 / apis 双授权 需要直接推送到商户账号下; 异步执行推送到商户逻辑
            SourceRegisterResultDTO pushResult = sourcePushResultInfoService.handlePushToMerchant(pushResultInfo);
            log.info("【线索注册】api注册类型，推送完整【pushResult】{}", JSON.toJSONString(pushResult));

            if (pushResult.isSuccess()) {
                // 推送成功，且不扣量，，，返回进件成功
                returnObject.put("status", "000");
                returnObject.put("remark", "进件成功");

                if (StrUtil.equals(InfoTypeEnum.APIS.getValue(), pushResultInfo.getInfoType())) {
                    MemberPushOrderInfo memberPushOrderInfo = memberPushOrderInfoService.getById(pushResultInfo.getOrderNo());

                    MerchantThirdBaseInfo thirdBaseInfo = merchantThirdBaseInfoService.selectEntityByOrderNo(memberPushOrderInfo.getPushId());
                    List<MerchantThirdProtocolInfo> thirdProtocolInfoList = merchantThirdProtocolInfoMapper.selectListByThirdId(thirdBaseInfo.getId());

                    returnObject.put("thirdBaseInfo", thirdBaseInfo);
                    returnObject.put("thirdProtocolInfoList", thirdProtocolInfoList);
                }
            } else {
                returnObject.put("status", "002");
                returnObject.put("remark", "进件失败");
            }
        }
        return ResultInfoVO.success(returnObject);
    }

    public String handleProtocolStorge(SourcePushResultInfo pushResultInfo, List<ProtocolInfo> protocolInfoList, MemberExtInfo memberExtInfo){
        // TODO 临时注释协议 240829
        // try {
        //     List<String> protocolUrlList = new ArrayList<>();
        //     // 第三方授权协议
        //     for (ProtocolInfo protocolInfo : protocolInfoList) {
        //         // 上传
        //         InputStream inputStream0 = new URL(protocolInfo.getProtocolUrl()).openStream();
        //         String fileKey0 = StrUtil.format("approveProtocol/channel/{}/{}/{}.html", pushResultInfo.getDateInfo(), pushResultInfo.getId(), protocolInfo.getProtocolName());
        //         String itemFileUrl = uploadProtocol(fileKey0, inputStream0);
        //         protocolUrlList.add(itemFileUrl);
        //     }
        //     return JSON.toJSONString(protocolUrlList);
        // }catch (Exception exception){
        //     exception.printStackTrace();
        // }
        return "";
    }

    @Autowired
    OssAliConfig ossAliConfig;
    @Autowired
    IOssService ossService;

    public String uploadProtocol(String fileKey, InputStream inputStream) throws Exception {

        OssAuthInfoParam ossAuthInfoParam = new OssAuthInfoParam();

        ossAuthInfoParam.setRegionId(ossAliConfig.getRegionId());
        ossAuthInfoParam.setBucketName(ossAliConfig.getBucketName());
        ossAuthInfoParam.setEndpoint(ossAliConfig.getEndpoint());
        ossAuthInfoParam.setHostUrl(ossAliConfig.getHostUrl());

        ossService.uploadFile(fileKey, inputStream, ossAuthInfoParam);

        String fileUrl = ossAliConfig.getHostUrl() + fileKey;
        return fileUrl;
    }




}
